//=============================================================================
// Project  : NIPO Component Framework
// File    : Service.cs
// Author  : Bernhard Dieber (Bernhard.Dieber@uni-klu.ac.at)
// Copyright 2010 by Bernhard Dieber
// This code is published under the Microsoft Public License (Ms-PL).  A copy
// of the license should be distributed with the code.  It can also be found
// at the project website: http://NIPO.CodePlex.com.   This notice, the
// author's name, and all copyright notices must remain intact in all
// applications, documentation, and source files.
//=============================================================================
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using NIPO.Data;
using NIPO.Plugins;
using NIPO.Util;

namespace NIPO.Controller
{
    /// <summary>
    /// Service discovery class.<br />
    /// This class supports generic and type-constrained service discovery.
    /// <para>
    /// Discovering generic services is done using serviceName, serviceDescription and sample input DTO.
    /// A service found by the generic mechanism should then be accessed by <see cref="ServicePlugin.PerformService"/>.<br /></para>
    /// <para>
    /// To find a concrete service it is also possible to search for a certain service class type. <see cref="Service.GetService&lt;T&gt;()"/> can be used to find a certain service type.
    /// This is useful if the concrete service class is known and/or if the service's functionality cannot be encapsulated into <see cref="ServicePlugin.PerformService"/> (i.e. this service may offer more members to perform its function).
    /// </para>
    /// In any way it is recommended to check the service compatibility using <see cref="ServicePlugin.CanProcess(DataTransferObject, out DataTransferObject)"/>.
    /// </summary>
    /// <seealso cref="Plugins.ServicePlugin"/>
    public static class Service
    {
        private static readonly List<ServicePlugin> Services = new List<ServicePlugin>();
        #region Service Discovery
        /// <summary>
        /// Gets a service by type. The exact type of service must be known. 
        /// </summary>
        /// <typeparam name="T">The implementing type of service</typeparam>
        /// <returns>The service instance if a suitable service was found, <c>null</c> otherwise.</returns>
        public static T GetService<T>() where T : ServicePlugin
        {
            foreach (ServicePlugin plugin in Services)
            {
                if (plugin is T)
                    return (T)plugin;
            }
            return null;
        }

        /// <summary>
        /// Gets a service by name.
        /// </summary>
        /// <param name="serviceName">Name of the service (regular expression).</param>
        /// <returns>The service instance if a suitable service was found, <c>null</c> otherwise.</returns>
        /// <exception cref="ArgumentException">Search for empty or null strings in service name</exception>
        public static ServicePlugin GetService(string serviceName)
        {
            if (string.IsNullOrEmpty(serviceName))
                throw new ArgumentException("Cannot search for empty or null strings in service name");
            Regex regex;
            try
            {
                regex = new Regex(serviceName);
            }
            catch (ArgumentException)
            {
                new Logger(typeof(Service).FullName).WriteErrorFormat("Illegal Regex Expression supplied for service name {0}", serviceName);
                return null;
            }
            foreach (ServicePlugin plugin in Services)
            {
                if (regex.Match(plugin.ServiceName).Success)
                    return plugin;
            }
            return null;
        }
        /// <summary>
        /// Gets a service by name and sample input. Every service matching the given <paramref name="serviceName"/> is tested for compatibility with the given <paramref name="input"/>
        /// using <see cref="ServicePlugin.CanProcess(DataTransferObject)"/>
        /// </summary>
        /// <param name="serviceName">Name of the service (regular expression).</param>
        /// <param name="input">A sample input to test a service for compatibility.</param>
        /// <returns>The service instance if a suitable service was found, <c>null</c> otherwise.</returns>
        /// <exception cref="ArgumentException">Search for empty or null strings in service name; <c>null</c> was supplied for <paramref name="input"/></exception>
        public static ServicePlugin GetService(string serviceName, DataTransferObject input)
        {
            if (string.IsNullOrEmpty(serviceName) || input == null)
                throw new ArgumentException("Servicename was null or empty or input was null");
            Regex regex;
            try
            {
                regex = new Regex(serviceName);
            }
            catch (ArgumentException)
            {
                new Logger(typeof(Service).FullName).WriteErrorFormat("Illegal Regex Expression supplied for service name {0}", serviceName);
                return null;
            }
            foreach (ServicePlugin plugin in Services)
            {
                if (regex.Match(plugin.ServiceName).Success && plugin.CanProcess(input))
                {
                    return plugin;
                }
            }
            return null;
        }

        /// <summary>
        /// Gets a service by name and description. A &amp;-association of <paramref name="serviceName"/> and <paramref name="serviceDescription"/> is used to determine a suitable service.
        /// </summary>
        /// <param name="serviceName">Name of the service (regular expression).</param>
        /// <param name="serviceDescription">The service description (regular expression).</param>
        /// <returns>The service instance if a suitable service was found, <c>null</c> otherwise.</returns>
        /// <exception cref="ArgumentException">Search for empty or null strings in <paramref name="serviceName"/> or <paramref name="serviceDescription"/></exception>
        public static ServicePlugin GetService(string serviceName, string serviceDescription)
        {
            if (string.IsNullOrEmpty(serviceName)||string.IsNullOrEmpty(serviceDescription))
                throw new ArgumentException("Search for empty or null strings in service name or service description");
            Regex nameRegex, descriptionRegex;
            try
            {
                nameRegex = new Regex(serviceName);
                descriptionRegex = new Regex(serviceDescription);
            }
            catch (ArgumentException)
            {
                new Logger(typeof(Service).FullName).WriteErrorFormat("Illegal Regex Expression supplied for service name {0} and service description {1}", serviceName, serviceDescription);
                return null;
            }
            foreach (ServicePlugin plugin in Services)
            {
                if (nameRegex.Match(plugin.ServiceName).Success&&descriptionRegex.Match(plugin.ServiceDescription).Success)
                    return plugin;
            }
            return null;
        }

        /// <summary>
        /// Gets ta service by name, description and sample input.
        /// </summary>
        /// <param name="serviceName">Name of the service (regular expression).</param>
        /// <param name="serviceDescription">The service description (regular expression).</param>
        /// <param name="input">A sample input to test a service for compatibility.</param>
        /// <returns>The service instance if a suitable service was found, <c>null</c> otherwise.</returns>
        /// <exception cref="ArgumentException">Search for empty or null strings in <paramref name="serviceName"/> or <paramref name="serviceDescription"/>; <paramref name="input"/> was <c>null</c></exception>
        public static ServicePlugin GetService(string serviceName, string serviceDescription, DataTransferObject input)
        {
            if (string.IsNullOrEmpty(serviceName) || string.IsNullOrEmpty(serviceDescription) || input == null)
                throw new ArgumentException("Search for empty or null strings in service name or service description or input was null");
            Regex nameRegex, descriptionRegex;
            try
            {
                nameRegex = new Regex(serviceName);
                descriptionRegex = new Regex(serviceDescription);
            }
            catch (ArgumentException)
            {
                new Logger(typeof(Service).FullName).WriteErrorFormat("Illegal Regex Expression supplied for service name {0} and service description {1}", serviceName, serviceDescription);
                return null;
            }
            foreach (ServicePlugin plugin in Services)
            {
                if (nameRegex.Match(plugin.ServiceName).Success && plugin.CanProcess(input) && descriptionRegex.Match(plugin.ServiceDescription).Success&&plugin.CanProcess(input))
                    return plugin;
            }
            return null;
        }
        #endregion

        #region Service Management
        /// <summary>
        /// Adds a service.
        /// </summary>
        /// <param name="service">The service.</param>
        internal static void AddService(ServicePlugin service)
        {
            if (!Services.Contains(service))
                Services.Add(service);
        }
        #endregion
    }
}